﻿/*
 * Jonathan
 * Created by: Jonathan Montiverdi
 * Date: 6/13/2012
 * License: http://jonathan.codeplex.com/license
 */
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Windows;

using Awesomium.Core;
using Jonathan.Data;
using Jonathan.Logic;
using Monti.MVVM.Services;

namespace Jonathan.Interface
{
   /// <summary>
   /// Interaction logic for Reader.xaml
   /// </summary>
   public partial class DataViewer
   {
      private IMain m_Main;
      private IDataViewerModel m_Model;
      private const string BASE_URL = "local://base_request.html/";
      
      /// <summary>
      /// Initializes a new reader object.
      /// </summary>
      public DataViewer()
      {
         InitializeComponent();
         this._WebControl.ResourceRequest += WebResourceRequest;
         this._WebControl.BeginNavigation += this.BeginNavigation;
         this.DataContextChanged += this.OnDataContenctChanged;
      }
      
      /// <summary>
      /// Gets or Sets the main application.
      /// </summary>
      public IMain Main { get { return this.m_Main ?? (this.m_Main = ServiceManager.Resolve <IMain> ()); } }
      
      /// <summary>
      /// Occurs when the data context has changed.
      /// </summary>
      /// <param name="sender">This</param>
      /// <param name="e">DependencyPropertyChangedEventArgs</param>
      private void OnDataContenctChanged (object sender, DependencyPropertyChangedEventArgs e)
      {
         if (e.NewValue == null) return;
         this.m_Model = this.DataContext as IDataViewerModel;
         if (this.m_Model == null)
            throw new InvalidOperationException ("Data context must be an IDataViewerModel object!");
         
         this.m_Model.HtmlChangeRequested += this.HtmlChangeRequested;
         this.m_Model.ItemClosing += this.Dispose;
         this.Main.Disposing += this.Dispose;
         this._WebControl.DomReady += WebControlDomReady;
         this._WebControl.LoadHTML (this.m_Model.Html);
      }
      
      /// <summary>
      /// Occurs when the html of the viewmodel changes.
      /// </summary>
      /// <param name="e">HtmlChangeEventArgs.</param>
      void HtmlChangeRequested (HtmlChangeEventArgs e)
      {
         if (e.TagId == "Html")
         {
            this._WebControl.DomReady += WebControlDomReady;
            MainViewModel.GuiDispatcher.Invoke (new Action <string> (content =>
                                                this._WebControl.LoadHTML (content)), e.Content);
         }
         else
         {
            var content = e.Content;
            if (e.Change == "html")
            {
               content = content.Replace ("\\'", "'").Replace ("'", "\\'").Replace ("\n", "").Replace ("\r", "");
               content = "'" + content + "'";
            }
            var javascript = string.Format ("$('#{0}').{1} ({2});", e.TagId, e.Change, content);
            MainViewModel.GuiDispatcher.Invoke (new Action <string> (js =>
                                                this._WebControl.ExecuteJavascript (js)), javascript);
         }
      }
      
      /// <summary>
      /// Occurs when the web control's dom has been parsed.
      /// </summary>
      /// <param name="sender">Web control.</param>
      /// <param name="e">Event args.</param>
      private void WebControlDomReady (object sender, EventArgs e)
      {
         this._WebControl.DomReady -= WebControlDomReady;
         var callbacks = this.m_Model.GetCallbacks ();
         if (callbacks != null && callbacks.Count > 0)
         {
            this._WebControl.CreateObject ("glObj");
            foreach (var callback in callbacks)
               this._WebControl.SetObjectCallback ("glObj", callback.Key, callback.Value);
         }
      }
      
      /// <summary>
      /// Occurs when the web control is requesting a resource.
      /// </summary>
      /// <param name="sender">Webcontrol.</param>
      /// <param name="e">ResourceRequestEventArgs</param>
      /// <returns>Requested resource.</returns>
      private static ResourceResponse WebResourceRequest (object sender, ResourceRequestEventArgs e)
      {
         if (e.Request.Url.Contains (BASE_URL))
         {
            if (!e.Request.Url.StartsWith (BASE_URL + "styles"))
            {
               e.Request.Cancel ();
               return null;
            }
            return new ResourceResponse (e.Request.Url.Replace (BASE_URL, Common.ApplicationPath));
         }

         try
         {
            if (e.Request.Url.Contains ("wikipedia"))
            {
               e.Request.Cancel ();
               Process.Start (e.Request.Url);
               return null;
            }
            // TODO: This is risky!!!
            // TODO: Cache results...
            var webRequest = (HttpWebRequest)WebRequest.Create (e.Request.Url);
            webRequest.Proxy.Credentials = CredentialCache.DefaultCredentials;
            webRequest.UseDefaultCredentials = true;
            var webResponse = webRequest.GetResponse ();
            var responseStream = webResponse.GetResponseStream ();
            if (responseStream != null)
            {
               byte[] response;
               if (e.Request.Url.EndsWith ("gif") || e.Request.Url.EndsWith ("jpg"))
               {
                  using (var reader = new BinaryReader (responseStream))
                     response = reader.ReadBytes (100000);
                  return new ResourceResponse (response, (e.Request.Url.EndsWith ("gif")) ? "image/gif" : "image/jpeg");
               }
               using (var reader = new StreamReader (responseStream, Encoding.UTF8))
                  response = Encoding.UTF8.GetBytes (reader.ReadToEnd ());
               return new ResourceResponse (response, "text/html");
            }
         }
         catch (Exception ex)
         {
            Console.WriteLine (ex.Message);
         }
         return null;
      }
      
      /// <summary>
      /// Occurs when the web control is navigating to a new url.
      /// </summary>
      /// <param name="sender">Webcontrol.</param>
      /// <param name="e">BeginNavigationEventArgs</param>
      private void BeginNavigation (object sender, BeginNavigationEventArgs e)
      {
         if (e.Url.Equals (BASE_URL)) return;
         this.m_Model.Open (e.Url.Replace (BASE_URL, ""));
      }
      
      /// <summary>
      /// Releases any resourses and saves the options.
      /// </summary>
      /// <param name="sender">IMain.</param>
      /// <param name="e">EventArgs</param>
      public void Dispose (object sender, EventArgs e)
      {
         this.Main.Disposing -= this.Dispose;
         this.m_Model.ItemClosing -= this.Dispose;
         this.m_Model.HtmlChangeRequested -= this.HtmlChangeRequested;
         
         if (this._WebControl != null)
         {
            this._WebControl.ResourceRequest -= WebResourceRequest;
            this._WebControl.BeginNavigation -= this.BeginNavigation;
         }
      }
   }
}